package cn.sccl.common.dao.impl;

import cn.sccl.common.model.*;
import cn.sccl.common.util.DataBaseUtils;
import cn.sccl.common.util.DevLog;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;

/**
 * Created by 杨浩宇 on 2016-02-13.
 */
@Repository(value = "iBatisGenericDao")
public class IBatisGenericDao<T> extends SqlSessionDaoSupport {

    public static final String POSTFIX_INSERT = ".insert";
    public static final String POSTFIX_UPDATE = ".update";
    public static final String POSTFIX_DELETE = ".delete";
    public static final String POSTFIX_DELETE_PRIAMARYKEY = ".deleteByPrimaryKey";
    public static final String POSTFIX_SELECT = ".select";
    public static final String POSTFIX_PAGE_SELECT = ".pageQuery";
    //    public static final String POSTFIX_SELECTMAP = ".selectByMap";
    public static final String POSTFIX_COUNT = ".count";
    public static final String POSTFIX_QUERY = ".query";
    /**
     * 插入设置主键对象
     */
    public static final String POSTFIX_INSERT_WITHKEY = ".insertWithObjectKey";
    /**
     * 逻辑批量删除
     */
    public static final String POSTFIX_LOGIC_DELETE = ".logicDelete";
    /**
     * 依据主键逻辑删除
     */
    public static final String POSTFIX_LOGIC_DELETE_PRIAMARYKEY = ".logicDeleteByPrimaryKey";
    /**
     * 更新部分字段
     */
    public static final String POSTFIX_UPDATE_PARTIAL_MODEL = ".updateMapModel";
    private static final int METADATA_PK_INDEX = 4;

    @Override
    @Autowired
    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        super.setSqlSessionTemplate(sqlSessionTemplate);
    }

    /**
     * 获取SQL映射文件的命名空间
     *
     * @return
     */
    protected String getSqlMapNamespace() {
        return "";
    }


    /**
     * 根据ID查询对象
     *
     * @param entityClass
     * @param id
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T get(Class<T> entityClass, Serializable id) {

        QueryData queryData = new QueryData();
        queryData.put("id", id);
        queryData.put("findIdentical", true);
        T objDB = (T) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_SELECT, queryData);
        return objDB;
    }


    /**
     * 查询所有记录
     *
     * @param entityClass
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getAll(Class<T> entityClass) {
        return getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_SELECT);
    }

    /**
     * 新增数据
     * mybatis提供id
     *
     * @param o
     */
    public void insert(Object o) {
        //考虑到数据库分区信息year新增的时候从数据库生成，缓存后因某些业务需要，故取服务器的年份
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        ((BaseModel) o).setYear(year);
        ((BaseModel) o).setCreateTime(new Timestamp(System.currentTimeMillis()));
        getSqlSession().insert(getSqlMapNamespace() + POSTFIX_INSERT_WITHKEY, o);

    }

    /**
     * 新增数据
     * 用户提供id
     *
     * @param o
     */
    public void insertWithPrimaryKey(Object o) {
        insert(o);

    }

    /**
     * 基于对象全字段更新数据，此方法调用后不会立即更新缓存，在事务结束时才更新缓存
     *
     * @param o
     */
    public void update(Object o) {
        this.update(o, Boolean.FALSE);
    }


    /**
     * 基于对象全字段更新数据，在此方法调用后立即更新缓存
     *
     * @param o
     * @param isupdateMC
     */
    public void update(Object o, boolean isupdateMC) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_UPDATE, o);
    }

    /**
     * 更新部分字段，此方法调用后不会立即更新缓存，在事务结束时才更新缓存
     * 对象属性不为空时，属性更新到数据库
     *
     * @param o
     */
    public void updatePartialModel(Object o) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_UPDATE_PARTIAL_MODEL, DataBaseUtils.Model2HashMap(o));
    }

    /**
     * 更新部分字段，此方法调用后不会立即更新缓存，在事务结束时才更新缓存
     * 对象属性不为空时，属性更新到数据库
     * 如果属性为空，且属性名称存在于aryNullFields数组中，则将数据库对应字段更新为null
     *
     * @param o
     * @param aryNullFields
     */
    public void updatePartialModel(Object o, String[] aryNullFields) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_UPDATE_PARTIAL_MODEL, DataBaseUtils.Model2HashMap(o, aryNullFields));
    }

    /**
     * 根据对象属性删除数据
     * 如果对象属性不为空，则参与到删除语句的where条件中
     *
     * @param o
     */
    @Deprecated
    public void remove(Object o) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_LOGIC_DELETE, o);
    }


    /**
     * 根据ID删除数据
     *
     * @param entityClass
     * @param id
     * @param <T>
     */
    public <T extends BaseModel> void removeById(Class<T> entityClass, Serializable id) {
        if (id == null) {
            throw new RuntimeException("id must not be null");
        }
        T obj = null;
        try {
            obj = entityClass.newInstance();
            obj.setId((String) id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_LOGIC_DELETE_PRIAMARYKEY, id);
    }


    /**
     * 根据条件查询数据，所有条件要求满足完全匹配，即where语句不会应用like条件
     *
     * @param entityClass
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> find(Class<T> entityClass, Map<String, Object> map) {
        if (map == null)
            return getAll(entityClass);
        DevLog.trace(map);
        map.put("findIdentical", "true");
        return this.getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_SELECT, map);
    }


    /**
     * 根据条件查询数据，字符串将应用like条件
     *
     * @param entityClass
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> findByLike(Class<T> entityClass, Map<String, Object> map) {
        if (map == null)
            return getAll(entityClass);
        DevLog.trace(map);
        return this.getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_SELECT, map);
    }


    /**
     * 根据参数查询一条唯一的记录
     *
     * @param entityClass
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T findUniqueBy(Class<T> entityClass, Map<String, Object> map) {
        try {
            map.put("findIdentical", "true");
            DevLog.trace(map);
            return (T) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_SELECT, map);
        } catch (Exception e) {
            logger.error("Error when propertie on entity," + e.getMessage(), e.getCause());
            throw new RuntimeException(e);
        }
    }


    public boolean isNotUnique(Object entity, String TABLE_NAME, String names) {
        try {
            String primarykey;
            Connection con = getSqlSession().getConnection();
            ResultSet dbMetaData = con.getMetaData().getPrimaryKeys(con.getCatalog(), null, TABLE_NAME);
            dbMetaData.next();
            if (dbMetaData.getRow() > 0) {
                primarykey = dbMetaData.getString(METADATA_PK_INDEX);
                if (names.indexOf(primarykey) > -1)
                    return false;
            } else {
                return true;
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
            return false;
        }
        return false;
    }


    /**
     * 兼容MSS的分页查询
     *
     * @param entityClass
     * @param query
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    @Deprecated
    public <T> List<T> query(Class<T> entityClass, BaseQuery query) {
        List<T> list = new ArrayList<T>();
        Integer num = null;

        num = (Integer) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_COUNT, query);

        if (num != null && num.intValue() != 0) {
            query.setTotal(num);
            list = getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_QUERY, query);
        }
        return list;
    }

    /**
     * 分页查询，所有条件要求满足完全匹配，即where语句不会应用like条件
     *
     * @param entityClass
     * @param pageIndex   页码
     * @param pageSize    分页大小
     * @param params
     * @param <T>
     * @return
     */
    public <T> PageResult<T> pageQuery(Class<T> entityClass, long pageIndex, int pageSize, Map<String, Object> params) {
        PageQueryData pageQueryData = new PageQueryData(pageIndex, pageSize);
        if (params != null && params.size() > 0) {
            pageQueryData.putAll(params);
        }
        pageQueryData.put("findIdentical", true);
        DevLog.trace(pageQueryData);
        Page<T> pageData = (Page<T>) this.getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_PAGE_SELECT, pageQueryData);
        return new PageResult<T>(pageData);
    }

    /**
     * 分页查询，字符串属性在where语句应用like条件
     *
     * @param entityClass
     * @param pageIndex   页码
     * @param pageSize    分页大小
     * @param params
     * @param <T>
     * @return
     */
    public <T> PageResult<T> pageQueryByLike(Class<T> entityClass, long pageIndex, int pageSize, Map<String, Object> params) {
        PageQueryData pageQueryData = new PageQueryData(pageIndex, pageSize);
        if (params != null && params.size() > 0) {
            pageQueryData.putAll(params);
        }
        DevLog.trace(pageQueryData);
        Page<T> pageData = (Page<T>) this.getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_PAGE_SELECT, pageQueryData);
        return new PageResult<T>(pageData);
    }

    /**
     * 根据参数查询数据，查询结果组装到Map，所有条件要求满足完全匹配，即where语句不会应用like条件
     *
     * @param entityClass
     * @param key
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> findMapBy(Class<T> entityClass, String key, Map<String, Object> map) {
        DevLog.trace(map);
        map.put("findIdentical", "True");
        return this.getSqlSession().selectMap(getSqlMapNamespace() + POSTFIX_SELECT, map, key);
    }

    /**
     * 根据参数查询数据，查询结果组装到Map，where语句中字符串自动应用like条件
     *
     * @param entityClass
     * @param key
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> findMapByLike(Class<T> entityClass, String key, Map<String, Object> map) {
        DevLog.trace(map);
        return this.getSqlSession().selectMap(getSqlMapNamespace() + POSTFIX_SELECT, map, key);
    }


    /**
     * @param <T>
     * @param entityClass
     * @param ids
     * @return
     * @Title: gets
     * @Description: 按照主键 对象的形式返回
     * @createdBy:raoifeng
     * @createaAt:2014-6-23下午10:20:26
     */
    public <T> List<T> gets(Class<T> entityClass, List<String> ids) {
        HashMap<String, T> returnMap = new HashMap<String, T>(ids.size());
        for (Serializable id : ids) {
            T objDB = (T) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_SELECT, id);
            if (objDB != null) {
                returnMap.put(id.toString(), objDB);
            }
        }
        List<T> listValue = new ArrayList<T>();
        Iterator it = returnMap.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next().toString();
            listValue.add(returnMap.get(key));
        }
        return listValue;
    }


}
